<?php

namespace App\Shx\Model;

use Storage;
use Illuminate\Support\Str;
use Illuminate\Contracts\Filesystem\Filesystem as FilesystemInterface;
use Illuminate\Contracts\Filesystem\FileNotFoundException;

class File
{

    const ANCHOR_BREAK = '@';
    const PATH_BREAK = '/';

    //filesystem
    protected $disk;
    protected $disk_name;
    protected $path;
    protected $path_to;
    protected $file;
    protected $file_name;
    protected $anchor;

    protected $metadata;

    protected $fs_proxy = [
        'mime_type'     => 'mimeType',
        'size'          => 'size',
        'last_modified' => 'lastModified',
        'exists'        => 'exists',
    ];

    public function __construct($uri, $file = null)
    {
        $this->parseUri($uri);
        $file and ($this->file = $file);
    }

    public function save()
    {
        if(empty($this->file)) {
            throw new \Exception('empty file content');
        }

        $this->file_name = $this->file->hashName();

        if(!$this->fileExists()) {
            $this->disk->putFileAs($this->path, $this->file, $this->file_name);
        }
        return $this;
    }
    
    public function get()
    {
        return $this->file;
    }
    
    public static function find($uri)
    {
        $model = new static($uri);

        return $model->fileExists() ? $model : null;
    }

    public static function findOrFail($uri)
    {
        if($model = self::find($uri)){
            return $model;
        } else {
            throw new FileNotFoundException;
        }
    }

    public static function create($uri, $file)
    {
        $model = new static($uri, $file);
        
        return $model->save();
    }

    protected function parseUri($uri)
    {
        $parse = explode(self::PATH_BREAK, $uri, 2);
        
        if(!isset($parse[0])){
            throw new Exception("Parse URI fail: {$uri}.");
        }
        
        $this->disk_name = $parse[0];
        $this->disk = Storage::disk($parse[0]);
        
        if(isset($parse[1])) {
            $parse = explode(self::PATH_BREAK, $parse[1]);

            $index = count($parse) - 1;

            $info = explode(self::ANCHOR_BREAK, $parse[$index], 2);

            $this->file_name = $info[0];
            $this->anchor = isset($info[1]) ? $info[1] : '';

            unset($parse[$index]);

            $this->path = self::PATH_BREAK.implode(self::PATH_BREAK, $parse); 


        }
        
        $this->path = $this->path ? $this->path : self::PATH_BREAK;
        
        return $this;
    }

    protected function fileExists()
    {
        return $this->disk->exists($this->path_to);
    }

    protected function getMetadataAndCache($key)
    {
        if(empty($this->metadata[$key])) {
            $this->metadata[$key] = $this->disk->{$this->fs_proxy[$key]}($this->path_to);
        }

        return $this->metadata[$key];
        throw new \ErrorException("Undefined property: ".__CLASS__."::$$key");
    }

    /**
     * Dynamically retrieve attributes on the model.
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        // $method = 'get'.ucfirst($key);
        // if(method_exists(self::class, $method)) {
        //     $this->
        // }
        switch($key){
            case array_key_exists($key, $this->fs_proxy) :
                return $this->getMetadataAndCache($key);
            case 'uri' :
                return $this->disk_name.$this->path_to;
            case 'content' :
                return $this->file;
            
        }
    }

    /**
     * Dynamically set attributes on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return void
     */
    public function __set($key, $value)
    {
        switch($key){
            case 'file_name' :
                return $this->path_to = $this->path.self::PATH_BREAK.$this->$value;
            
        }
    }
}
